#include "router.hh"

#include <iostream>

using namespace std;

// Dummy implementation of an IP router

// Given an incoming Internet datagram, the router decides
// (1) which interface to send it out on, and
// (2) what next hop address to send it to.

// For Lab 6, please replace with a real implementation that passes the
// automated checks run by `make check_lab6`.

// You will need to add private members to the class declaration in `router.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

//! \param[in] route_prefix The "up-to-32-bit" IPv4 address prefix to match the datagram's destination address against
//! \param[in] prefix_length For this route to be applicable, how many high-order (most-significant) bits of the route_prefix will need to match the corresponding bits of the datagram's destination address?
//! \param[in] next_hop The IP address of the next hop. Will be empty if the network is directly attached to the router (in which case, the next hop address should be the datagram's final destination).
//! \param[in] interface_num The index of the interface to send the datagram out on.
void Router::add_route(const uint32_t route_prefix,
                       const uint8_t prefix_length,
                       const optional<Address> next_hop,
                       const size_t interface_num) {
    cerr << "DEBUG: adding route " << Address::from_ipv4_numeric(route_prefix).ip() << "/" << int(prefix_length)
         << " => " << (next_hop.has_value() ? next_hop->ip() : "(direct)") << " on interface " << interface_num << "\n";

    // Your code here.
    _route_table.push_back(_select_route{   route_prefix,
                                            prefix_length,
                                            next_hop,
                                            interface_num   });
}

//! \param[in] dgram The datagram to be routed
void Router::route_one_datagram(InternetDatagram &dgram) {
    // Your code here.
    uint32_t dst = dgram.header().dst;
    uint8_t max_prefix = 0; 
    _select_route match_route = {0, 33};
    // 寻找符合最大前缀匹配的路由
    for (auto &match : _route_table) {
        auto mask = 0xFFFFFFFF << (32-match._prefix_length);
        if (match._prefix_length == 0) {
            mask = 0;
        }
        if ((dst & mask) == (match._route_prefix & mask)) {
            if (match._prefix_length >= max_prefix) {
                max_prefix = match._prefix_length;
                match_route = match;
            }
        }    
    }     

    // 没找到就直接返回
    if (match_route._prefix_length > 32) {   /* 通关最后一卡
                                                记录一下，这里最后还卡了一下
                                                最开始是判断max_prefix是否为0，但确实有prefix_length为0的情况
                                                即如果没有匹配的prefix,就往不需要匹配的（prefix-length==0）网络走 */
      return;
    }

    // 如果找到，但ttl过小，直接返回
    if (dgram.header().ttl <= 1) {
        dgram.header().ttl = 0;
        return;
    }
    dgram.header().ttl --;

    // 转发发送
    if (match_route._next_hop.has_value()) /* 这里是学的@Mr小明
                                                optional类型要用value才能
                                                进行类似赋值等的值操作 */
        {
        _interfaces[match_route._interface_num].send_datagram(dgram, match_route._next_hop.value()); 
    } 
    else { /* 这里其实也是看的@Mr小明，c++不太懂...
               这种情况有点像自定义路由表，目的interface由数据报决定 */
        _interfaces[match_route._interface_num].send_datagram(dgram,
                                                    Address::from_ipv4_numeric(dgram.header().dst));
    }
}

void Router::route() {
    // Go through all the interfaces, and route every incoming datagram to its proper outgoing interface.
    for (auto &interface : _interfaces) {
        auto &queue = interface.datagrams_out();
        while (not queue.empty()) {
            route_one_datagram(queue.front());
            queue.pop();
        }
    }
}
